﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DataProcessor.Core;

namespace DataProcessor.FashionData
{
    public class SMaxEvaluator
    {
        private IList<ClassifiedImage> _images;
        
        public static void Run()
        {
            var sme = new SMaxEvaluator(@"D:\Data\Dropbox\University\S-Max\Final Results\MergedResults.csv");
            //sme.CalculateAccuracy();
            //sme.CalculateAccuracy();
            //sme.PrintConfusionMatrix(im => im.Domino);
            sme.CopyImages(@"D:\Data\Dropbox\University\S-Max\Final Results\confusion\1.txt",
                @"D:\Data\Datasets\Fashion10000\Photos", @"C:\Users\Bob\Desktop\temp\1");


            //sme.ProduceDominoMajorityVote(@"D:\Data\Dropbox\University\S-Max\Final Results\DominoResults.txt",
            //    @"D:\Data\Dropbox\University\S-Max\Final Results\DominoResults.csv");

            //sme.MergeDominoAndClassifierResults(@"D:\Data\Dropbox\University\S-Max\Final Results\DominoResults.csv",
            //    @"D:\Data\Dropbox\University\S-Max\Final Results\Classifier_Result_csv.csv",
            //    @"D:\Data\Dropbox\University\S-Max\Final Results\MergedResults.csv");

            
        }

        public SMaxEvaluator()
        {
            _images = new List<ClassifiedImage>();
        }

        public SMaxEvaluator(string mergedResultsFile)
        {
            _images = LoadClassifiedImages(mergedResultsFile);
        }
        
        public void ProduceDominoMajorityVote(string resultFile, string outputFile)
        {
            var output = File.ReadAllLines(resultFile).Select(l => 
            {
                string[] parts = l.Split(' ');
                return new { Image = parts[0], Category = GetMajorityVote(parts.Skip(1).ToArray()) };
            }).Select(i => string.Format("{0},{1}", i.Image, (int) Enum.Parse(typeof(ClothesCategory), i.Category)));

            File.WriteAllLines(outputFile, output);
        }

        public string GetMajorityVote(string[] seq)
        {
            return seq.GroupBy(i => i).OrderByDescending(g => g.Count()).Take(1).Single().Key;
        }

        public void MergeDominoAndClassifierResults(string dominoResult, string classifierResult, string outputFile)
        {
            var domino = File.ReadAllLines(dominoResult).ToCsvDictionary()
                .Select(i => new { Image = i["Image"], Category = i["Decision"] });

            var classifier = File.ReadAllLines(classifierResult).ToCsvDictionary()
                .Select(i => new { Image = i["Image"], Category = i["Decision"], TrueClass = i["TrueClass"] });

            var output = domino.Join(classifier, d => d.Image, c => c.Image,
                (d, c) => new { Image = d.Image, Domino = d.Category, Classifier = c.Category, TrueClass = c.TrueClass })
                .Select(i => string.Format("{0},{1},{2},{3}", i.Image, i.TrueClass, i.Classifier, i.Domino));

            var header = new string[] { "Image,TrueClass,Classifier,Domino" };
            File.WriteAllLines(outputFile, header.Concat(output));
        }

        private List<ClassifiedImage> LoadClassifiedImages(string mergedResults)
        {
            return File.ReadAllLines(mergedResults).ToCsvDictionary()
                .Select(i => new ClassifiedImage { ImageId = i["Image"], Classifier = i["Classifier"], Domino = i["Domino"], TrueClass = i["TrueClass"] })
                .ToList();
        }

        public void CalculateAccuracy()
        {
            double classifierAccuracy = (double)_images.Where(i => i.Classifier == i.TrueClass).Count() / _images.Count();
            double dominoAccuracy = (double) _images.Where(i => i.Domino == i.TrueClass).Count() / _images.Count();

            Console.WriteLine(string.Format("Classifier accuracy: {0:0.0000}", classifierAccuracy));
            Console.WriteLine(string.Format("Domino accuracy: {0:0.0000}", dominoAccuracy));
        }

        public void CalculateAveragePrecision()
        {
            var classifierPrecision = Enumerable.Range(0, 4).Select(i =>
                {
                    int tp = _images.Where(im => im.TrueClass == i.ToString() && im.Classifier == im.TrueClass).Count();
                    int fp = _images.Where(im => im.TrueClass != i.ToString() && im.Classifier == i.ToString()).Count();
                    return (double)tp / (tp + fp);
                });

            Console.WriteLine("Classifier Precesion:");
            classifierPrecision.ToList().ForEach(p => Console.WriteLine(string.Format("{0:0.0000}", p)));

            var dominoPrecision = Enumerable.Range(0, 4).Select(i =>
            {
                int tp = _images.Where(im => im.TrueClass == i.ToString() && im.Domino == im.TrueClass).Count();
                int fp = _images.Where(im => im.TrueClass != i.ToString() && im.Domino == i.ToString()).Count();
                return (double)tp / (tp + fp);
            });

            Console.WriteLine("Domino Precesion:");
            dominoPrecision.ToList().ForEach(p => Console.WriteLine(string.Format("{0:0.0000}", p)));
        }

        public void PrintConfusionMatrix(Func<ClassifiedImage,string> selector)
        {
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    Console.Write(_images.Where(im => im.TrueClass == i.ToString() && selector(im) == j.ToString()).Count() + "\t");
                }
                Console.WriteLine();
            }
        }

        public void CopyImages(string imgList, string searchDir, string destination)
        { 
            File.ReadAllLines(imgList).ToList()
                .ForEach(im => Directory.GetFiles(searchDir, im, SearchOption.AllDirectories).ToList()
                    .ForEach(f => File.Copy(f, destination + "\\" + im)));
        }
    }

    public class ClassifiedImage
    {
        public string ImageId { get; set; }
        public string Classifier { get; set; }
        public string Domino { get; set; }
        public string TrueClass { get; set; }
    }

    enum ClothesCategory
    { 
        InformalWomen = 0,
        FormalWomen = 1,
        InformalMen = 2,
        FormalMen = 3
    }
}
